/**************************************************************************/
/*                                                                        */
/*                                 OCaml                                  */
/*                                                                        */
/*             Xavier Leroy, projet Cristal, INRIA Rocquencourt           */
/*                                                                        */
/*   Copyright 1996 Institut National de Recherche en Informatique et     */
/*     en Automatique.                                                    */
/*                                                                        */
/*   All rights reserved.  This file is distributed under the terms of    */
/*   the GNU Lesser General Public License version 2.1, with the          */
/*   special exception on linking described in the file LICENSE.          */
/*                                                                        */
/**************************************************************************/

/* Buffered input/output */

#ifndef CAML_IO_H
#define CAML_IO_H

#ifdef CAML_INTERNALS

#include "camlatomic.h"
#include "misc.h"
#include "mlvalues.h"

#ifndef _MSC_VER
#include "platform.h"
#else
/* We avoid including platform.h (which is really only necessary here to declare
   caml_plat_mutex) because that would end up pulling in pthread.h but we want
   to hide it on the MSVC port as it is not the native way to handle threads.
   So we inline here just the implementation of caml_plat_mutex on that port,
   this should be kept in sync */
#include <stdint.h>
typedef intptr_t caml_plat_mutex;
#endif

#if defined(_WIN32)
typedef int64_t file_offset;
#else
#include <sys/types.h>
typedef off_t file_offset;
#endif

struct channel {
  int fd;                       /* Unix file descriptor */
  file_offset offset;           /* Absolute position of fd in the file */
  char * end;                   /* Physical end of the buffer */
  char * curr;                  /* Current position in the buffer */
  char * max;                   /* Logical end of the buffer (for input) */
  caml_plat_mutex mutex;        /* Mutex protecting buffer */
  struct channel * next, * prev;/* Double chaining of channels (flush_all) */
  uintnat refcount;             /* Number of custom blocks owning the channel */
  int flags;                    /* Bitfield */
  char * buff;                  /* The buffer */
  char * name;                  /* Optional name (to report fd leaks) */
};

enum {
  CHANNEL_FLAG_FROM_SOCKET = 1,  /* For Windows */
  CHANNEL_FLAG_MANAGED_BY_GC = 4,  /* Free and close using GC finalization. */
  /* Note: For backwards-compatibility, channels without the flag
     CHANNEL_FLAG_MANAGED_BY_GC can be used inside single-threaded
     programs without locking. As a consequence, using such a channel
     from an asynchronous callback can result in deadlocks. */
  CHANNEL_TEXT_MODE = 8, /* "Text mode" for Windows and Cygwin */
  CHANNEL_FLAG_UNBUFFERED = 16     /* Unbuffered (for output channels only) */
};

/* For an output channel:
     [offset] is the absolute position of the beginning of the buffer [buff].
   For an input channel:
     [offset] is the absolute position of the logical end of the buffer, [max].
*/

/* Creating and closing channels from C */

CAMLextern void caml_close_channel (struct channel *);
CAMLalloc(caml_close_channel, 1) CAMLreturns_nonnull()
CAMLextern struct channel * caml_open_descriptor_in (int);
CAMLalloc(caml_close_channel, 1) CAMLreturns_nonnull()
CAMLextern struct channel * caml_open_descriptor_out (int);
CAMLextern file_offset caml_channel_size (struct channel *);
CAMLextern void caml_seek_in (struct channel *, file_offset);
CAMLextern void caml_seek_out (struct channel *, file_offset);
CAMLextern file_offset caml_pos_in (struct channel *);
CAMLextern file_offset caml_pos_out (struct channel *);

/* I/O on channels from C. The channel must be locked (see below) before
   calling any of the functions and macros below */

CAMLextern value caml_alloc_channel(struct channel *chan);
CAMLextern int caml_channel_binary_mode (struct channel *);

CAMLextern int caml_flush_partial (struct channel *);
CAMLextern void caml_flush (struct channel *);
CAMLextern void caml_flush_if_unbuffered (struct channel *);
CAMLextern void caml_putch(struct channel *, int);
CAMLextern void caml_putword (struct channel *, uint32_t);
CAMLextern int caml_putblock (struct channel *, const char *, intnat);
CAMLextern void caml_really_putblock (struct channel *, const char *, intnat);

CAMLextern unsigned char caml_refill (struct channel *);
CAMLextern unsigned char caml_getch(struct channel *);
CAMLextern uint32_t caml_getword (struct channel *);
CAMLextern int caml_getblock (struct channel *, char *, intnat);
CAMLextern intnat caml_really_getblock (struct channel *, char *, intnat);

/* Extract a struct channel * from the heap object representing it */

#define Channel(v) (*((struct channel **) (Data_custom_val(v))))

/* The locking machinery */

CAMLextern void caml_channel_lock(struct channel *);
CAMLextern void caml_channel_unlock(struct channel *);

/* Lock and Unlock are compatibility aliases for OCaml<5.2.
   Remove whenever 5.2 is old enough. (See #12792.) */
#define Lock(channel) caml_channel_lock(channel)
#define Unlock(channel) caml_channel_unlock(channel)

CAMLextern void caml_channel_cleanup_on_raise(void);

CAMLextern struct channel * caml_all_opened_channels;

/* Conversion between file_offset and int64_t */

#define Val_file_offset(fofs) caml_copy_int64(fofs)
#define File_offset_val(v) ((file_offset) Int64_val(v))

/* Primitives required by the Unix library */
CAMLextern value caml_ml_open_descriptor_in(value fd);
CAMLextern value caml_ml_open_descriptor_out(value fd);
CAMLextern value caml_ml_open_descriptor_in_with_flags(int fd, int flags);
CAMLextern value caml_ml_open_descriptor_out_with_flags(int fd, int flags);

#endif /* CAML_INTERNALS */

#endif /* CAML_IO_H */
